API Gatewayにクライアント証明書による認証を設定してみる
CX事業本部@大阪の岩田です。
2020/9/17付けのアップデートによりAPI Gatewayでクライアント証明書による認証がサポートされるようになりました。
すでにAWSブログの方でクライアント証明書による認証を利用する手順が紹介されているので、こちらのブログを参考にしつつクライアント証明書による認証を設定してみたいと思います。
前提条件
クライアント証明書による認証を設定するためにはAPI Gatewayのカスタムドメイン名が必須となります。以下の記事を参考にカスタムドメインを設定したAPI Gatewayを用意しておいて下さい。
カスタムドメインが設定できたら、一度カスタムドメインを利用してAPIが実行できることを確認しておきましょう。
$ curl -s -o /dev/null -w '%{http_code}\n' https://<設定したカスタムドメイン名> 200
200OKが返ってくれば準備完了です。
クライアント証明書の発行
ここからはクライアント証明書による認証を設定するためにクライアント証明書を発行していきます。
CAの構築
まずはルートCAを構築します。
ルートCAの秘密鍵を作成します。
$openssl genrsa -out RootCA.key 4096 Generating RSA private key, 4096 bit long modulus .........++ .......++ e is 65537 (0x10001)
続いて自己署名CA証明書を作成します。
$ openssl req -new -x509 -days 36500 -key RootCA.key -out RootCA.pem You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) []:JP State or Province Name (full name) []:OSAKA Locality Name (eg, city) []:HIGOBASHI Organization Name (eg, company) []:Classmethod Organizational Unit Name (eg, section) []:CX Division Common Name (eg, fully qualified host name) []:cm-iwata ca Email Address []:
CAの名前は「cm-iwata ca」としました。今回は特に中間CAは利用しないので、以上でCAの準備は完了です。
クライアントの秘密鍵とCSRの作成
続いてクライアント証明書を準備していきます。
まずはクライアント側の秘密鍵を作成します。
$ openssl genrsa -out my_client.key 2048 Generating RSA private key, 2048 bit long modulus ......................................+++ ............+++ e is 65537 (0x10001)
続いて先程作成した秘密鍵をもとにCSRを作成します。
$ openssl req -new -key my_client.key -out my_client.csr You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) []:JP State or Province Name (full name) []:OSAKA Locality Name (eg, city) []:HIGOBASHI Organization Name (eg, company) []:Classmethod Organizational Unit Name (eg, section) []:CX Division Common Name (eg, fully qualified host name) []:cm-iwata client Email Address []: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []:
クライアントの名前は「cm-iwata client」としました。
クライアント証明書の発行
準備ができたのでクライアント証明書を発行します。
$ openssl x509 -req -in my_client.csr -CA RootCA.pem -CAkey RootCA.key -set_serial 01 -out my_client.pem -days 36500 -sha256 Signature ok subject=/C=JP/ST=OSAKA/L=HIGOBASHI/O=Classmethod/OU=CX Division/CN=cm-iwata client Getting CA Private Key
これで準備OKです。
API Gatewayにクライアント証明書による認証を設定
ここからAPI Gatewayにクライアント証明書による認証を設定していきます。
ルートCA証明書をS3にアップ
クライアント証明書の妥当性を検証するためには、クライアント証明書を発行したCAの証明書をAPI Gatewayに登録しておく必要があります。このCA証明書はAPI Gatewayと同一リージョンのS3から読み込む必要があるので、先程作成したCA証明書を適当なS3バケットにアップします。
$ aws s3 cp RootCA.pem s3://<適当なS3バケット>
クライアント証明書による認証を有効化
続いてAPI Gatewayのカスタムドメインの設定から「Mutual TLS authentication」をONにし、「Trusostore URI」に先程S3にアップしたCAを証明書のパスを指定します。本来はS3のバージョニングを有効にしてバージョンまで指定するのが推奨ですが、今回はお試しなのでバージョン指定は省略しています。
しばらく待つとステータスが「Available」に変わります。
これで準備完了です。
クライアント証明書を使ってAPI Gatewayにアクセスする
API Gatewayの準備ができたので、まずは先程と同様にクライアント証明書無しでAPI Gatewayにアクセスしてみます。
$ curl https://<設定したカスタムドメイン名> curl: (35) LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to <設定したカスタムドメイン名>:443
エラーになりました。
今度はクライアント証明書と秘密鍵の指定を追加してアクセスしてみます。
curl -i --key my_client.key --cert my_client.pem https://<設定したカスタムドメイン名> HTTP/2 200 x-amzn-requestid: f6340999-4e61-423d-ae27-919b4e093f5c x-amz-apigw-id: TMO90GCVNjMFWgA= x-amzn-trace-id: Root=1-5f67f858-21bfaab0db325647f17eb067;Sampled=0 content-type: application/json content-length: 3179 date: Mon, 21 Sep 2020 00:48:24 GMT {"resource": "/", "path": "/", "httpMethod": "GET", "headers": {"accept": "*/*", ...略
無事に200OKでレスポンスが返却されてきました。
なお、今回API GatewayのバックエンドにはeventデータをそのままJSONで返却するだけのLambdaを紐付けています。Lambdaから返却されてきたイベントデータを確認すると、以下のようにrequestContext
のidentity
以下にclientCert
という項目が設定されていることが分かります。
{ ...略 "requestContext": { ...略 "identity": { "cognitoIdentityPoolId": null, "clientCert": { "clientCertPem": "<クライアント証明書の内容>", "serialNumber": "1", "issuerDN": "C=JP,ST=OSAKA,L=HIGOBASHI,O=Classmethod,OU=CX Division,CN=cm-iwata ca", "validity": { "notAfter": "Aug 28 00:31:30 2120 GMT", "notBefore": "Sep 21 00:31:30 2020 GMT" }, "subjectDN": "C=JP,ST=OSAKA,L=HIGOBASHI,O=Classmethod,OU=CX Division,CN=cm-iwata client" }, "cognitoIdentityId": null, "principalOrgId": null, "cognitoAuthenticationType": null, "userArn": null, "userAgent": "curl/7.64.1", "accountId": null, "caller": null, "sourceIp": "126.83.27.201", "accessKey": null, "cognitoAuthenticationProvider": null, "user": null } } }
subjectDN
からクライアントの名前が取得できるので、後続のLambdaで各種データストアの情報と突き合わせて独自のビジネスロジックを実装するといったことも可能になります。
(おまけ)API Gatewayのデフォルトエンドポイントを無効に
カスタムドメインに対してクライアント証明書による認証を設定しましたが、API Gatewayデフォルトのエンドポイント(https://
curl --location --request PATCH 'https://apigateway.ap-northeast-1.amazonaws.com/restapis/<API ID>' \ --header 'X-Amz-Content-Sha256: <ハッシュ値>' \ --header 'X-Amz-Date: 20200921T012547Z' \ --header 'Authorization: AWS4-HMAC-SHA256 Credential=<アクセスキーID>/20200921/ap-northeast-1/apigateway/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=<SIGV4の署名>' \ --header 'Content-Type: application/json' \ --data-raw '{"patchOperations": [{"op": "replace", "path": "/disableExecuteApiEndpoint", "value": "true"}]}'
{"patchOperations": [{"op": "replace", "path": "/disableExecuteApiEndpoint", "value": "true"}]}
の部分でデフォルトのエンドポイントを無効化するように指定しています。
実行成功後にしばらく待つとデフォルトのエンドポイントが利用できなくなります。
$ curl https://<API-ID>.execute-api.ap-northeast-1.amazonaws.com/dev {"message":"Forbidden"}
まとめ
API Gatewayにクライアント証明書による認証を設定してみました。API Gatewayがクライアント証明書による認証をサポートしたことで、IoT機器からAPIを実行するようなユースケースにおけるアーキテクチャの選択肢が広がったのではないでしょうか?選択肢の1つとしてうまく活用していきたいですね。